package ltd.dujiabao.inventoryservice.listener;

import lombok.extern.slf4j.Slf4j;
import ltd.dujiabao.common.event.BillingFailedEvent;
import ltd.dujiabao.common.event.InventoryDeductedEvent;
import ltd.dujiabao.common.event.InventoryFailedEvent;
import ltd.dujiabao.inventoryservice.dao.InventoryLogRepository;
import ltd.dujiabao.inventoryservice.dao.InventoryRepository;
import ltd.dujiabao.inventoryservice.domain.entity.InventoryLog;
import ltd.dujiabao.inventoryservice.domain.enums.TransactionType;
import ltd.dujiabao.common.event.CreateOrderEvent;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

/**
 * @author Bowie Du
 * @date 2025/7/14 20:11
 */
@Slf4j
@Component
public class InventoryListener {

    @Autowired
    private InventoryRepository inventoryRepository;

    @Autowired
    private InventoryLogRepository inventoryLogRepository;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @RabbitListener(
            bindings = @QueueBinding(
                    value = @Queue(
                            value = "inventory.queue",
                            durable = "true"
                    ),
                    exchange = @Exchange(
                            value = "order.success.exchange",
                            durable = "true",
                            type = ExchangeTypes.FANOUT
                    )
            )
    )
    @Transactional(rollbackFor = Exception.class)
    public void handleCreateOrderEvent(CreateOrderEvent event) {
        // 扣减库存
        boolean isSuccess = inventoryRepository.deductStock(event.getProductId(), event.getQuantity()) > 0;

        // 生成流水
        if (isSuccess) {
            InventoryLog inventoryLog = new InventoryLog()
                    .setQuantity(event.getQuantity())
                    .setProductId(event.getProductId())
                    .setOrderId(event.getOrderId())
                    .setTransactionType(TransactionType.DEDUCT.name())
                    .setCreatedAt(LocalDateTime.now())
                    .setUpdatedAt(LocalDateTime.now());
            inventoryLogRepository.save(inventoryLog);
        }

        // 发布事件
        if (isSuccess) {
            InventoryDeductedEvent deductedEvent = InventoryDeductedEvent.builder()
                    .orderId(event.getOrderId())
                    .userId(event.getUserId())
                    .productId(event.getProductId())
                    .quantity(event.getQuantity())
                    .amount(event.getAmount())
                    .build();
            rabbitTemplate.convertAndSend("inventory.success.exchange", "", deductedEvent);
        } else {
            InventoryFailedEvent failedEvent = InventoryFailedEvent.builder()
                    .orderId(event.getOrderId())
                    .reason("Failed to deduct inventory.")
                    .build();
            rabbitTemplate.convertAndSend("inventory.fail.exchange", "", failedEvent);
        }

        log.info("deduct stock orderId {} result {}", event.getOrderId(), isSuccess);
    }

    @RabbitListener(
            bindings = @QueueBinding(
                    value = @Queue(
                            value = "inventory.billing.fail.queue",
                            durable = "true"
                    ),
                    exchange = @Exchange(
                            value = "billing.fail.exchange",
                            durable = "true",
                            type = ExchangeTypes.FANOUT
                    )
            )
    )
    @Transactional(rollbackFor = Exception.class)
    public void handleBillingFailedEvent(BillingFailedEvent event) {
        // 查出该订单的所有流水记录
        List<InventoryLog> inventoryLogs = inventoryLogRepository.findByOrderId(event.getOrderId());
        Optional<InventoryLog> deductLogOptional = inventoryLogs.stream()
                .filter(inventoryLog -> TransactionType.DEDUCT.name().equals(inventoryLog.getTransactionType()))
                .findFirst();
        Optional<InventoryLog> restoreLogOptional = inventoryLogs.stream()
                .filter(inventoryLog -> TransactionType.RESTORE.name().equals(inventoryLog.getTransactionType()))
                .findFirst();

        // 幂等性
        if (restoreLogOptional.isPresent() || deductLogOptional.isEmpty()) {
            log.info("no need to rollback, orderId {}", event.getOrderId());
            return;
        }

        // 回退库存
        InventoryLog deductLog = deductLogOptional.get();
        boolean isSuccess = inventoryRepository.restoreStock(deductLog.getProductId(), deductLog.getQuantity()) > 0;

        // 保存回退流水
        if (isSuccess) {
            InventoryLog restoreLog = new InventoryLog()
                    .setQuantity(deductLog.getQuantity())
                    .setProductId(deductLog.getProductId())
                    .setOrderId(event.getOrderId())
                    .setTransactionType(TransactionType.RESTORE.name())
                    .setCreatedAt(LocalDateTime.now())
                    .setUpdatedAt(LocalDateTime.now());
            inventoryLogRepository.save(restoreLog);
        }
        log.info("rollback stock orderId {} result {}", event.getOrderId(), isSuccess);
    }
}
